#ifndef _BUILD_HELPER_H_
#define _BUILD_HELPER_H_

#include <stdint.h>
#include <string>

namespace bs_helper {

template<int i = 0>
class bs_endian {
public:
    static bool is_little() { return s_is_little_; }

private:
    union endian_helper {
    public:
        endian_helper(short _vl) { a = _vl; }
        short a;
        char b;
    };
    static bool s_is_little_;
};

template<int i>
bool bs_endian<i>::s_is_little_ = (1 == bs_endian::endian_helper(1).b);

class bs_in_stream {

public:
    bs_in_stream(uint32_t _length = 0, const uint8_t* _data = NULL)
        : offset_(0)
        , length_(_length)
        , data_(_data) {}
    ~bs_in_stream() {}

public:
    bool child_stream(uint32_t _length, bs_in_stream& _child) {
        if (left() < _length) {
            return false;
        }
        _child = bs_in_stream(_length, data());
        shift(_length);
        return true;
    }

    template<class _BASE_T>
    bool read(_BASE_T& _vl) {
        if (left() < sizeof(_vl)) {
            return false;
        }
        if (bs_endian<>::is_little()) {
            uint8_t transfor[sizeof(_vl)] = {0};
            for (int i = 0; i < sizeof(_vl); ++i) {
                transfor[sizeof(_vl) - 1 - i] = *(data() + i);
            }
            _vl = *((_BASE_T*)transfor);
        } else {
            _vl = *((_BASE_T*)data());
        }
        shift(sizeof(_vl));
        return true;
    }
    template<class _BASE_T>
    bool read(_BASE_T* _vl, uint32_t _len) {
        if (NULL == _vl || 0 == _len) {
            return true;
        }
        uint32_t l_need_len = sizeof(_BASE_T) * _len;
        if (left() < l_need_len) {
            return false;
        }
        memcpy_s(_vl, l_need_len, data(), l_need_len);
        shift(l_need_len);
        return true;
    }

protected:
    const uint8_t* data() const {
        return data_ + offset_;
    }
    uint32_t left() const {
        return length_ - offset_;
    }
    void shift(uint32_t _len) {
        offset_ += _len;
    }
    
private:
    uint32_t offset_ = 0;
    uint32_t length_ = 0;

    const uint8_t* data_ = nullptr;
};

class bs_out_stream {
public:
    bs_out_stream(uint32_t _length, uint8_t* _data)
        : offset_(0)
        , length_(_length)
        , data_(_data) {}

public:
    template<class _BASE_T>
    bool write(const _BASE_T& _vl) {
        if (left() < sizeof(_vl)) {
            return false;
        }
        if (bs_endian<>::is_little()) {
            uint8_t transfor[sizeof(_vl)] = {0};
            *((_BASE_T*)transfor) = _vl;
            for (int i = 0; i < sizeof(_vl); ++i) {
                *(data() + sizeof(_vl) - 1 - i) = transfor[i];
            }
        } else {
            *((_BASE_T*)data()) = _vl;
        }
        shift(sizeof(_vl));
        return true;
    }
    template<class _BASE_T>
    bool write(const _BASE_T* _vl, uint32_t _len, uint32_t _shift) {
        if (0 == _shift) {
            return true;
        }
        uint32_t l_need_len = sizeof(_BASE_T) * _shift;
        if (left() < l_need_len) {
            return false;
        }
        if (NULL != _vl && _len > 0 && _shift > 0) {
            memcpy_s(data(), left(), _vl, (_len > _shift) ? l_need_len : sizeof(_BASE_T) * _len);
        }
        shift(l_need_len);
        return true;
    }
    template<class _BASE_T>
    bool write(const _BASE_T* _vl, uint32_t _len) {
        return write(_vl, _len, _len);
    }

protected:

    uint8_t* data() {
        return data_ + offset_;
    }
    uint32_t left() const {
        return length_ - offset_;
    }
    void shift(uint32_t _len) {
        offset_ += _len;
    }

private:
    uint32_t offset_ = 0;
    uint32_t length_ = 0;

    uint8_t* data_ = nullptr;
};

} // namespace bs_helper

#endif